package symbol;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

public class ScopeStack {
    public static BufferedWriter symbolWriter;
    private static ScopeStack instance;
    private final Stack<SymbolTable> stack = new Stack<>();
    // 临时List，用于存放作业要求的输出
    private final List<SymbolTable> symbolList = new ArrayList<>();
    private int scopeLevel = 1;// 全局只有一个
    private int scopeNumber = 1;// 全局只有一个

    private ScopeStack() {
    }

    public static ScopeStack getInstance() {
        if (instance == null) {
            instance = new ScopeStack();
        }
        return instance;
    }

    public int getScopeNumberBefore() {
        // 返回当前栈顶所在的作用域编号
        return stack.peek().getScopeNumber();
    }

    public int getScopeNumberAfter() {
        // 返回当前栈顶所在的作用域层次
        return symbolList.get(symbolList.size() - 1).getScopeNumber();
    }

    public void initBuffer(String outputFile) throws IOException {
        symbolWriter = new BufferedWriter(new FileWriter(outputFile));
    }

    public void closeBuffer() throws IOException {
        symbolWriter.close();
    }

    public void push(SymbolTable symbolTable) {
        stack.push(symbolTable);
    }

    public SymbolTable pop() {
        return stack.pop();
    }

    // 进入新作用域
    public void enterScope() {
        if (stack.isEmpty()) {
            // 创建全局作用域
            SymbolTable table = new SymbolTable(scopeLevel, scopeNumber);
            stack.push(table);
        } else {
            scopeLevel++;
            scopeNumber++;
            SymbolTable table = new SymbolTable(scopeLevel, scopeNumber);
            stack.push(table);
        }
    }

    // 退出当前作用域
    public void exitScope() {
        // 只退出作用域，不将其弹出
        // 这时候需要将Level减一，序号不减一
        scopeLevel--;
        symbolList.add(stack.pop());
    }

    // 从弹出的List中寻找Number为num的SymbolTable
    public SymbolTable getSymbolTableFromList(int num) {
        for (SymbolTable table : symbolList) {
            if (table.getScopeNumber() == num) {
                return table;
            }
        }
        return null;
    }

    // 将一个符号表添加到栈中
    public void addSymbolTable(int num) {
        SymbolTable table = getSymbolTableFromList(num);
        symbolList.remove(table);
        stack.push(table);
    }

    // 从栈中弹出一个符号表
    public void popSymbolTable() {
        symbolList.add(stack.pop());
    }

    // 向当前作用域添加符号
    public boolean addSymbol(Symbol symbol) {
        return stack.peek().addSymbol(symbol);
    }

    // 存储基本类型的符号
    public void addSymbol(String name, SymbolType type, SymbolKind kind, int value) {
        BasicSymbol symbol = new BasicSymbol(name, type, kind, scopeLevel, scopeNumber, value);
        addSymbol(symbol);
    }

    public void addSymbol(String name, SymbolType type, SymbolKind kind) {
        BasicSymbol symbol = new BasicSymbol(name, type, kind, scopeLevel, scopeNumber);
        addSymbol(symbol);
    }

    // 存储数组大小已知的数组
    public void addSymbol(String name, SymbolType type, SymbolKind kind, int size, List<Integer> valueList) {
        ArraySymbol symbol = new ArraySymbol(name, type, size, kind, scopeLevel, scopeNumber);
        addSymbol(symbol);
    }

    // 存储函数符号
    public void addSymbol(String name, SymbolType type, int paramNum, List<Symbol> paramList) {
        FuncSymbol symbol = new FuncSymbol(name, type, paramNum, scopeLevel, scopeNumber, paramList);
        addSymbol(symbol);
    }

    // 从当前作用域中查找符号
    public Symbol getSymbol(String name) {
        if (stack.peek().contains(name)) {
            return stack.peek().getSymbol(name);
        }
        return null;
    }

    // 从当前作用域及其上层作用域中查找符号
    public Symbol lookup(String name) {
        for (int i = stack.size() - 1; i >= 0; i--) {
            SymbolTable table = stack.get(i);
            Symbol symbol = table.getSymbol(name);
            if (symbol != null) {
                return symbol;
            }
        }
        return null;
    }

    // 从上层作用域中查找符号
    public Symbol lookupBefore(String name) {
        for (int i = stack.size() - 2; i >= 0; i--) {
            SymbolTable table = stack.get(i);
            Symbol symbol = table.getSymbol(name);
            if (symbol != null) {
                return symbol;
            }
        }
        return null;
    }

    // 输出符号表
    public void showSymbolTable() throws IOException {
        // 按照scopeNumber; 符号所在层次编号输出
        symbolList.sort((o1, o2) -> o1.getScopeNumber() - o2.getScopeNumber());
        for (SymbolTable table : symbolList) {
            table.show();
        }
    }
}
